#!/usr/bin/python
# -*- coding: UTF-8 -*-

import json
import sys
import os

class FileGenerator:
    def __init__(self, file_name, cpp_json, pb_json = None):
        self.file_name = file_name
        self.file_name_split = file_name.split(".")
        self.cpp_json = cpp_json
        self.pb_json = pb_json
        self.gen_struct_pb_file()
        self.gen_pb_serializer_file()
        self.gen_struct_orm_func()
        
    def gen_struct_pb_file(self):
        file = open(self.file_name_split[0] + ".service.proto", "w")
        file.write("// Machine generated code\n\n")
        file.write('syntax = "proto3";\n\n')
        file.write("package " + self.file_name_split[0] + ";\n\n")
        if "enums" in self.cpp_json:
            for enum in self.cpp_json["enums"]:
                file.write("enum " + enum["name"] + " {\n")
                file.write("    default_" + enum["name"] + " = 0;\n")
                if "fields" in enum:
                    for field in enum["fields"]:
                        file.write("    " + field["name"] + " = " + str(field["value"]) + ";\n")
                file.write("}\n\n")
        if ("structs" in self.pb_json) and (self.pb_json["structs"] is not None):
            for struct in self.pb_json["structs"]:
                file.write("message " + struct["name"] + " {\n")
                n = 1
                for field in struct["fields"]:
                    if field["IdlType"] == "dict":
                        file.write("    " + field["type"] + "<" + field["key"]["type"] + "," +   field["value"]["type"] + "> " + field["name"] + " = " + str(n) + ";\n")
                    elif field["IdlType"] == "seq" or field["IdlType"] == "set":
                        file.write("    " + field["type"] + " " + field["key"]["type"] + " " + field["name"] + " = " + str(n) + ";\n")
                    else:
                        file.write("    " + field["type"] + " " + field["name"] + " = " + str(n) + ";\n")
                    n += 1
                file.write("}\n\n")
        self.gen_method_pb_file(file)
        self.gen_method_ret_pb(file)
        file.close()
        os.system("protoc --cpp_out=. " + self.file_name_split[0] + ".service.proto")
        
    def gen_method_ret_pb(self, file):
        if (not ("services" in self.pb_json)) or (self.pb_json["services"] is None):
            return
        if "services" in self.pb_json:
            for service in self.pb_json["services"]:
                for method in service["methods"]:
                    file.write("message " + service["name"] + "_" + method["name"] + "_ret {\n")
                    if method["retType"]["IdlType"] == "dict":
                        file.write("    " + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," +   method["retType"]["value"]["type"] + "> " + " _1 = 1;\n")
                    elif method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                        file.write("    " + method["retType"]["type"] + " " + method["retType"]["key"]["type"] + " _1 = 1;\n")
                    elif method["retType"]["IdlType"] == "void":
                        pass
                    else:
                        file.write("    " + method["retType"]["type"] + " _1 = 1;\n")
                    file.write("}\n\n")

    def gen_struct_orm_func(self):
        if not ("structs" in self.pb_json) or (self.pb_json["structs"] is None):
            return
        file = open(self.file_name_split[0] + ".orm.h", "w")
        file.write("// Machine generated code\n\n")
        file.write('#pragma once\n\n')
        file.write('#include "'+self.file_name_split[0]+'.struct.h"\n')
        file.write("#include <string>\n\n")
        file.write('namespace '+self.file_name_split[0]+'_orm {\n\n')
        for struct in self.pb_json["structs"]:
            file.write("bool serialize(const " + struct["name"] + "& msg, std::string& buffer);\n")
            file.write("bool deserialize(const std::string& buffer,"+ struct["name"] + "& msg);\n")
        file.write('\n}\n')
        file_cpp = open(self.file_name_split[0] + ".orm.cpp", "w")
        file_cpp.write('#include "'+self.file_name_split[0]+'.orm.h"\n')
        for struct in self.pb_json["structs"]:
            file_cpp.write('#include "'+struct['name']+'_struct.h"\n')
        file_cpp.write('#include "' + self.file_name_split[0] + '.service.pb.h"\n\n')
        self.gen_struct_pb_serializer(file_cpp)
        self.gen_pb_struct_serializer(file_cpp)
        file_cpp.write('namespace '+self.file_name_split[0]+'_orm {\n\n')
        for struct in self.pb_json["structs"]:
            file_cpp.write("bool serialize(const " + struct["name"] + "& msg, std::string& buffer) {\n")
            file_cpp.write('    '+self.file_name_split[0] + "::" + struct["name"] + " pb_msg;\n")
            for field in struct["fields"]:
                if field["IdlType"] == "dict":
                    file_cpp.write("    for (auto& [k, v] : msg." + field["name"] + ") {\n")
                    if field["value"]["IdlType"] == "struct":
                        file_cpp.write("        serialize(v, (*pb_msg.mutable_" + field["name"] + "())[k]);\n")
                    elif field["value"]["IdlType"] == "enum":
                        file_cpp.write("        (*pb_msg.mutable_" + field["name"] + "())[k] =("+self.file_name_split[0]+"::"+ field["value"]["type"]+")v;\n")
                    else:
                        file_cpp.write("        (*pb_msg.mutable_" + field["name"] + "())[k] = v;\n")
                    file_cpp.write("    }\n")
                elif field["IdlType"] == "seq" or field["IdlType"] == "set":
                    file_cpp.write("    for (auto& i : msg." + field["name"] + ") {\n")
                    if field["key"]["IdlType"] == "struct":
                        file_cpp.write("        serialize(i, *pb_msg.add_" + field["name"] + "());\n")
                    elif field["key"]["IdlType"] == "enum":
                        file_cpp.write("        pb_msg.add_" + field["name"] + "(("+ self.file_name_split[0]+"::"+field["key"]["type"]+")i);\n")
                    else:
                        file_cpp.write("        pb_msg.add_" + field["name"] + "(i);\n")
                    file_cpp.write("    }\n")
                elif field["IdlType"] == "struct":
                    file_cpp.write("    serialize(msg." + field["name"] + ", *pb_msg.mutable_" + field["name"] + "());\n")
                elif field["IdlType"] == "enum":
                    file_cpp.write("    pb_msg.set_" + field["name"] + "(("+ self.file_name_split[0] + "::" + field["type"]+")msg." +  field["name"] + ");\n")
                else:
                    file_cpp.write("    pb_msg.set_" + field["name"] + "(msg." +  field["name"] + ");\n")
            file_cpp.write("    auto byte_size = pb_msg.ByteSizeLong();\n")
            file_cpp.write("    buffer.resize(byte_size);\n")
            file_cpp.write("    return pb_msg.SerializeToString(&buffer);\n")
            file_cpp.write("}\n\n")
            file_cpp.write("bool deserialize(const std::string& buffer, "+ struct["name"] + "& msg) {\n")
            file_cpp.write('    '+self.file_name_split[0] + "::" + struct["name"] + " pb_msg;\n")
            file_cpp.write("    if (!pb_msg.ParseFromString(buffer)) { return false; }\n")
            for field in struct["fields"]:
                if field["IdlType"] == "dict":
                    fieldName = "pb_msg." + field["name"]
                    file_cpp.write("    for (auto it = " + fieldName + "().begin(); it != " + fieldName + "().end(); it++) {\n")
                    if field["value"]["IdlType"] == "struct":
                        file_cpp.write("        serialize(it->second, msg." + field["name"] + "[it->first]);\n")
                    else:
                        file_cpp.write("        msg." + field["name"] + "[it->first] = it->second;\n")
                    file_cpp.write("    }\n")
                elif field["IdlType"] == "seq":
                    file_cpp.write("    msg." + field["name"] + ".resize(pb_msg." + field["name"] + "_size());\n")
                    file_cpp.write("    for (int i = 0; i < pb_msg." + field["name"] + "_size(); i++) {\n")
                    if field["key"]["IdlType"] == "struct":
                        file_cpp.write("        serialize(pb_msg." + field["name"] + "(i), msg." + field["name"]+ "[i]);\n")
                    else:
                        file_cpp.write("        msg." + field["name"] + "[i] = pb_msg." + field["name"] + "(i);\n")
                    file_cpp.write("    }\n")
                elif field["IdlType"] == "set":
                    file_cpp.write("    for (int i = 0; i < pb_msg." + field["name"] + "_size(); i++) {\n")
                    if field["key"]["IdlType"] == "struct":
                        file_cpp.write("        serialize(pb_msg." + field["name"] + "(i), msg." + field["name"]+ "[i]);\n")
                    else:
                        file_cpp.write("        msg." + field["name"] + ".insert(pb_msg." + field["name"] + "(i));\n")
                    file_cpp.write("    }\n")
                elif field["IdlType"] == "struct":
                    file_cpp.write("    serialize(pb_msg." + field["name"] + "(), msg." + field["name"] + ");\n")
                elif field["IdlType"] == "enum":
                    file_cpp.write("    msg." + field["name"] + " = ("+ self.file_name_split[0] +"_cpp::"+ field["type"] +")pb_msg." +  field["name"] + "();\n")
                else:
                    file_cpp.write("    msg." + field["name"] + " = pb_msg." +  field["name"] + "();\n")
            file_cpp.write("    return true;\n")
            file_cpp.write("}\n\n")
        file_cpp.write('}\n')
        
    def gen_struct_pb_serializer(self, file):
        if ("structs" in self.pb_json) and (self.pb_json["structs"] is not None):
            for struct in self.pb_json["structs"]:
                file.write("void serialize(const " + struct["name"] + "& msg, " + self.file_name_split[0] + "::" + struct["name"] + "& pb_msg);\n")
            file.write("\n")
        if ("structs" in self.pb_json) and (self.pb_json["structs"] is not None):
            for struct in self.pb_json["structs"]:
                file.write("void serialize(const " + struct["name"] + "& msg, " + self.file_name_split[0] + "::" + struct["name"] + "& pb_msg) {\n")
                for field in struct["fields"]:
                    if field["IdlType"] == "dict":
                        file.write("    for (auto& [k, v] : msg." + field["name"] + ") {\n")
                        if field["value"]["IdlType"] == "struct":
                            file.write("        serialize(v, (*pb_msg.mutable_" + field["name"] + "())[k]);\n")
                        elif field["value"]["IdlType"] == "enum":
                            file.write("        (*pb_msg.mutable_" + field["name"] + "())[k] = ("+ self.file_name_split[0] +"::"+ field["value"]["type"] +") v;\n")
                        else:
                            file.write("        (*pb_msg.mutable_" + field["name"] + "())[k] = v;\n")
                        file.write("    }\n")
                    elif field["IdlType"] == "seq" or field["IdlType"] == "set":
                        file.write("    for (auto& i : msg." + field["name"] + ") {\n")
                        if field["key"]["IdlType"] == "struct":
                            file.write("        serialize(i, *pb_msg.add_" + field["name"] + "());\n")
                        elif field["key"]["IdlType"] == "enum":
                            file.write("        pb_msg.add_" + field["name"] + "(("+ self.file_name_split[0] +"::"+ field["key"]["type"] +")i);\n")
                        else:
                            file.write("        pb_msg.add_" + field["name"] + "(i);\n")
                        file.write("    }\n")
                    elif field["IdlType"] == "struct":
                        file.write("    serialize(msg." + field["name"] + ", *pb_msg.mutable_" + field["name"] + "());\n")
                    elif field["IdlType"] == "enum":
                        file.write("    pb_msg.set_" + field["name"] + "(("+ self.file_name_split[0] + "::" + field["type"]+")msg." +  field["name"] + ");\n")
                    else:
                        file.write("    pb_msg.set_" + field["name"] + "(msg." +  field["name"] + ");\n")
                file.write("}\n\n")
            
    def gen_pb_struct_serializer(self, file):
        if ("structs" in self.pb_json) and (self.pb_json["structs"] is not None):
            for struct in self.pb_json["structs"]:
                file.write("void serialize(const " + self.file_name_split[0] + "::" + struct["name"] + "& pb_msg, " +  struct["name"] + "& msg);\n")
            file.write("\n")
        if ("structs" in self.pb_json) and (self.pb_json["structs"] is not None):
            for struct in self.pb_json["structs"]:
                file.write("void serialize(const " + self.file_name_split[0] + "::" + struct["name"] + "& pb_msg, " +  struct["name"] + "& msg) {\n")
                for field in struct["fields"]:
                    if field["IdlType"] == "dict":
                        fieldName = "pb_msg." + field["name"]
                        file.write("    for (auto it = " + fieldName + "().begin(); it != " + fieldName + "().end(); it++) {\n")
                        if field["value"]["IdlType"] == "struct":
                            file.write("        serialize(it->second, msg." + field["name"] + "[it->first]);\n")
                        elif field["value"]["IdlType"] == "enum":
                             file.write("        msg." + field["name"] + "[it->first] = ("+ self.file_name_split[0] +"_cpp::"+ field["value"]["type"] +")it->second;\n")
                        else:
                            file.write("        msg." + field["name"] + "[it->first] = it->second;\n")
                        file.write("    }\n")
                    elif field["IdlType"] == "seq":
                        file.write("    msg." + field["name"] + ".resize(pb_msg." + field["name"] + "_size());\n")
                        file.write("    for (int i = 0; i < pb_msg." + field["name"] + "_size(); i++) {\n")
                        if field["key"]["IdlType"] == "struct":
                            file.write("        serialize(pb_msg." + field["name"] + "(i), msg." + field["name"]+ "[i]);\n")
                        elif field["key"]["IdlType"] == "enum":
                            file.write("        msg." + field["name"] + "[i] = ("+ self.file_name_split[0] +"_cpp::" + field["key"]["type"] +")pb_msg." + field["name"] + "(i);\n")
                        else:
                            file.write("        msg." + field["name"] + "[i] = pb_msg." + field["name"] + "(i);\n")
                        file.write("    }\n")
                    elif field["IdlType"] == "set":
                        file.write("    for (int i = 0; i < pb_msg." + field["name"] + "_size(); i++) {\n")
                        if field["key"]["IdlType"] == "struct":
                            file.write("        serialize(pb_msg." + field["name"] + "(i), msg." + field["name"]+ "[i]);\n")
                        elif field["key"]["IdlType"] == "enum":
                            file.write("        msg." + field["name"] + ".insert(("+ self.file_name_split[0] + "_cpp::"+ field["key"]["type"] +")pb_msg." + field["name"] + "(i));\n")
                        else:
                            file.write("        msg." + field["name"] + ".insert(pb_msg." + field["name"] + "(i));\n")
                        file.write("    }\n")
                    elif field["IdlType"] == "struct":
                        file.write("    serialize(pb_msg." + field["name"] + "(), msg." + field["name"] + ");\n")
                    elif field["IdlType"] == "enum":
                        file.write("    msg." + field["name"] + " = ("+ self.file_name_split[0] +"_cpp::"+ field["type"] +")pb_msg." +  field["name"] + "();\n")
                    else:
                        file.write("    msg." + field["name"] + " = pb_msg." +  field["name"] + "();\n")
                file.write("}\n\n")
            
    def gen_method_pb_file(self, file):
        if (not ("services" in self.pb_json)) or (self.pb_json["services"] is None):
            return
        if "services" in self.pb_json:
            for service in self.pb_json["services"]:
                for method in service["methods"]:
                    file.write("message " + service["name"] + "_" + method["name"] + "_args {\n")
                    n = 1
                    for arg in method["arguments"]:
                        if arg["IdlType"] == "dict":
                            file.write("    " + arg["type"] + "<" + arg["key"]["type"] + "," +   arg["value"]["type"] + ">")
                        elif arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                            file.write("    " + arg["type"] + " " + arg["key"]["type"])
                        elif arg["IdlType"] == "void":
                            continue
                        else:
                            file.write("    " + arg["type"])
                        file.write(" _" + str(n) + " = " + str(n) + ";\n") 
                        n += 1
                    file.write("}\n\n")
                
    def gen_pb_serializer_file(self):
        if (not ("services" in self.pb_json)) or (self.pb_json["services"] is None):
            return
        for service in self.cpp_json["services"]:
            file = open(self.file_name_split[0] + ".service." + service["name"] + ".proxy.serializer.cpp", "w")
            file.write("// Machine generated code\n\n")
            file.write("#define _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS\n\n")
            file.write('#include "' + self.file_name_split[0] + '.service.pb.h"\n')
            file.write('#include "rpc_root.h"\n')
            file.write('#include "rpc_proxy.h"\n')
            file.write('#include "rpc_stub.h"\n\n')
            file.write('#include "' + self.file_name_split[0] + ".service." + service["name"] + '.proxy.serializer.h"\n\n')      
            file_stub = open(self.file_name_split[0] + ".service." + service["name"] + ".stub.serializer.cpp", "w")            
            file_stub.write("// Machine generated code\n\n")
            if service["type"] != "generic":
                file_stub.write("#define _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS\n\n")
                file_stub.write('#include "' + self.file_name_split[0] + '.service.pb.h"\n')
                file_stub.write('#include "' + self.file_name_split[0] + ".service." + service["name"] + '.stub.serializer.h"\n\n')
            if service["loadType"] == "dynamic":
                if service["type"] != "generic":
                    file_stub.write('#include "rpc_root.h"\n')
                file.write('#include "rpc_proxy.h"\n')
                file.write('#include "rpc_stub.h"\n\n')
            else:
                if service["type"] != "generic":
                    file_stub.write('#include "rpc_root.h"\n')
                file.write('#include "rpc_proxy.h"\n')
                file.write('#include "rpc_stub.h"\n\n')
            if ("structs" in self.cpp_json) and (self.cpp_json["structs"] is not None):
                for struct in self.cpp_json["structs"]:
                    file_stub.write('#include "'+ struct["name"] +'_struct.h"\n')
                    file.write('#include "'+ struct["name"] +'_struct.h"\n')
            file.write("\n")
            file.write("namespace " + self.file_name_split[0] + service["name"] + "_proxy {\n\n")
            file.write("char buffer[rpc::STREAMBUF_SIZE] = {0};\n\n")     
            self.gen_struct_pb_serializer(file)
            self.gen_pb_struct_serializer(file)
            file.write('}\n\n')
            file.write("using namespace " + self.file_name_split[0] + service["name"] + "_proxy;\n\n")
            if service["type"] != "generic":
                file_stub.write("namespace " + self.file_name_split[0] + service["name"] + "_stub {\n\n")
                file_stub.write("char buffer[rpc::STREAMBUF_SIZE] = {0};\n\n")     
                self.gen_struct_pb_serializer(file_stub)
                self.gen_pb_struct_serializer(file_stub)
                file_stub.write('}\n\n')
                file_stub.write("using namespace " + self.file_name_split[0] + service["name"] + "_stub;\n\n")
            for method in service["methods"]:
                self.gen_args_pb_serializer_file(service, method, file)
            file.write("\n")
            for method in service["methods"]:
                self.gen_method_args_serialize(service, method, file)
            for method in service["methods"]:
                self.gen_method_ret_deserialize(service, method, file)
            for method in service["methods"]:
                self.gen_method_ret_coro_deserialize(service, method, file)
            for method in service["methods"]:
                self.gen_method_stub_deserializer(service, method, file_stub)
            for method in service["methods"]:
                self.gen_method_ret_serialize(service, method, file_stub)
            file.close()
            file_stub.close()
            
    def gen_args_pb_serializer_file(self, service, method, file):
        file.write("void serialize(" + self.file_name_split[0] + "::" + service["name"] + "_" + method["name"] + "_args& args")
        for arg in method["arguments"]:
            if arg["IdlType"] == "void":
                break;
            if arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                fkey = arg["key"]["type"]
                if arg["key"]["IdlType"] == "enum": 
                    fkey = self.file_name_split[0] + "_cpp::" + fkey
                file.write(", const " + arg["type"] + "<" + fkey + ">& _" + str(arg["index"]))
            elif arg["IdlType"] == "dict":
                fvalue = arg["value"]["type"]
                if arg["value"]["IdlType"] == "enum":
                    fvalue = self.file_name_split[0] + "_cpp::" + fvalue
                file.write(", const " + arg["type"] + "<" + arg["key"]["type"] + "," + fvalue + ">& _" + str(arg["index"]))
            elif arg["IdlType"] == "struct" or arg["IdlType"] == "string":
                file.write(", const " + arg["type"] + "& _" + str(arg["index"]))
            elif arg["IdlType"] == "enum":
                file.write(", " + self.file_name_split[0] + "_cpp::" + arg["type"] + " _" + str(arg["index"]))
            else:
                file.write(", " + arg["type"] + " _" + str(arg["index"]))
        file.write(") {\n")
        n = 1
        for arg in method["arguments"]:
            if arg["IdlType"] == "void":
                break;
            if arg["IdlType"] == "seq":
                if arg["key"]["IdlType"] == "struct":
                    file.write("    for (auto& i : _" + str(n) + ") {\n")
                    file.write("        serialize(i, *args.add__" + str(n) + "());\n")
                    file.write("    }\n")
                else:
                    file.write("    for (auto& i : _" + str(n) + ") {\n")
                    if arg["key"]["IdlType"] == "enum":
                        file.write("        args.add__" + str(n) + "((" + self.file_name_split[0] + "::" + arg["key"]["type"] + ")i);\n")
                    else:
                        file.write("        args.add__" + str(n) + "(i);\n")
                    file.write("    }\n")
            elif arg["IdlType"] == "set":
                file.write("    for (auto& i : _" + str(n) + ") {\n")
                if arg["key"]["IdlType"] == "enum":
                    file.write("        args.add__" + str(n) + "((" + self.file_name_split[0] + "::" + arg["key"]["type"] + ")i);\n")
                else: 
                    file.write("        args.add__" + str(n) + "(i);\n")
                file.write("    }\n")
            elif arg["IdlType"] == "struct":
                file.write("    serialize(_" + str(n) + ", " + "*args.mutable__" + str(n) + "());\n")
            elif arg["IdlType"] == "dict":
                file.write("    for (auto& [k, v] : _" + str(n) + ") {\n")
                if arg["value"]["IdlType"] ==  "struct":
                    file.write("        serialize(v, (*args.mutable__" + str(n) + "())[k]);\n")
                else:
                    if arg["value"]["IdlType"] == "enum":
                        file.write("        (*args.mutable__" + str(n) + "())[k] = (" + self.file_name_split[0] + "::" + arg["value"]["type"] + ")v;\n")
                    else:
                        file.write("        (*args.mutable__" + str(n) + "())[k] = v;\n")
                file.write("    }\n")
            elif arg["IdlType"] == "enum":
                file.write("    args.set__" + str(n) + "((" + self.file_name_split[0] +  "::" + arg["type"] + ")_" + str(n) + ");\n")
            else:
                file.write("    args.set__" + str(n) + "(_" + str(n) + ");\n")
            n += 1
        file.write("}\n\n")
                
    def gen_method_args_serialize(self, service, method, file):
        file.write("void serialize_" + service["name"] + "_" + method["name"] + "(rpc::Proxy* proxy, rpc::ProxyCallPtr callPtr, rpc::MethodID methodID, rpc::TransportPtr transport")
        for arg in method["arguments"]:
            if arg["IdlType"] == "void":
                break
            if arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                fkey = arg["key"]["type"]
                if arg["key"]["IdlType"] == "enum":
                    fkey = self.file_name_split[0]+"_cpp::"+fkey
                file.write(", const " + arg["type"] + "<" + fkey + ">& _" + str(arg["index"]))
            elif arg["IdlType"] == "dict":
                fvalue = arg["value"]["type"]
                if arg["value"]["IdlType"] == "enum":
                    fvalue = self.file_name_split[0] + "_cpp::" + fvalue
                file.write(", const " + arg["type"] + "<" + arg["key"]["type"] + "," + fvalue + ">& _" + str(arg["index"]))
            elif arg["IdlType"] == "struct" or arg["IdlType"] == "string":
                file.write(", const " + arg["type"] + "& _" + str(arg["index"]))
            elif arg["IdlType"] == "enum":
                file.write(", "+ self.file_name_split[0] + "_cpp::" + arg["type"] + " _" + str(arg["index"]))
            else:
                file.write(", " + arg["type"] + " _" + str(arg["index"]))
        file.write(") {\n")
        file.write("    " + self.file_name_split[0] + "::" + service["name"] + "_" + method["name"]  + "_args pb_args;\n")
        file.write("    serialize(pb_args")
        for arg in method["arguments"]:
            if arg["IdlType"] == "void":
                break
            arg_name = ", _" + str(arg["index"])
            file.write(arg_name)
        file.write(");\n")
        file.write("    if (proxy->getGlobalIndex() == rpc::INVALID_GLOBAL_INDEX) {\n")
        file.write("        auto* header = reinterpret_cast<rpc::RpcCallHeader*>(buffer);\n")
        file.write("        std::size_t byteSize = pb_args.ByteSizeLong();\n")
        file.write("        std::size_t length = sizeof(rpc::RpcCallHeader) + byteSize;\n")
        file.write("        rpc::buildHeader(*header, proxy, callPtr ? callPtr->getCallID() : 0, methodID, (std::uint32_t)byteSize);\n")
        file.write("        if (!pb_args.SerializeToArray(buffer + sizeof(rpc::RpcCallHeader), sizeof(buffer) - sizeof(rpc::RpcCallHeader))) {\n")
        file.write('            Logbegin << "Proxy serializing failed, [" << proxy->getSignature(methodID) << "]" << Logend;\n')
        file.write('            Logbegin << "Protobuf SerializeToArray failed, [" << pb_args.GetDescriptor()->name() << "]" << Logend;\n')
        file.write("            return;\n")
        file.write('        }\n')
        file.write("        transport->send(buffer, (int)length);\n")
        file.write("        if (callPtr && (callPtr->getRetry() > 0)) {\n")
        file.write("            callPtr->setData(buffer, (int)length);\n")
        file.write("        }\n")
        file.write("    } else {\n")
        file.write("        auto* header = reinterpret_cast<rpc::RpcProxyCallHeader*>(buffer);\n")
        file.write("        std::size_t byteSize = pb_args.ByteSizeLong();\n")
        file.write("        std::size_t length = sizeof(rpc::RpcProxyCallHeader) + byteSize;\n")
        if "oneway" in method:
            file.write("        rpc::buildHeader(*header, proxy, callPtr ? callPtr->getCallID() : 0, methodID, (std::uint32_t)byteSize, true);\n")
        else:
            file.write("        rpc::buildHeader(*header, proxy, callPtr ? callPtr->getCallID() : 0, methodID, (std::uint32_t)byteSize, false);\n")
        file.write("        if (!pb_args.SerializeToArray(buffer + sizeof(rpc::RpcProxyCallHeader), sizeof(buffer) - sizeof(rpc::RpcProxyCallHeader))) {\n")
        file.write('            Logbegin << "Proxy serializing failed, [" << proxy->getSignature(methodID) << "]" << Logend;\n')
        file.write('            Logbegin << "Protobuf SerializeToArray failed, [" << pb_args.GetDescriptor()->name() << "]" << Logend;\n')
        file.write("            return;\n")
        file.write('        }\n')
        file.write("        transport->send(buffer, (int)length);\n")
        file.write("        if (callPtr && (callPtr->getRetry() > 0)) {\n")
        file.write("            callPtr->setData(buffer, (int)length);\n")
        file.write("        }\n")
        file.write("    }\n")
        file.write("}\n\n")
        
    def gen_method_ret_deserialize(self, service, method, file):
        if "oneway" in method:
            return
        file.write("void deserialize_" + service["name"] + "_" + method["name"] + "(" + service["name"] + "_" + method["name"] + "_callback callback, rpc::TransportPtr trans, int size, rpc::RpcError errorID) {\n")
        file.write("    " + self.file_name_split[0] + "::" + service["name"] + "_" + method["name"] + "_ret ret;\n")
        file.write("    trans->recv(buffer, size);\n")
        file.write("    if (!ret.ParseFromArray(buffer, size)) {\n")
        file.write('        Logbegin << "Proxy deserializing failed, [' + service["name"] + '::' + method["name"] +']" << Logend;\n')
        file.write('        Logbegin << "Protobuf SerializeToArray failed, [" << ret.GetDescriptor()->name() << "]" << Logend;\n')
        file.write("        return;\n")
        file.write("    }\n")
        if method["retType"]["IdlType"]  == "seq":
            file.write("    " + method["retType"]["type"]  + "<" + method["retType"]["key"]["type"] + "> methodRet(ret._1_size());\n")
            file.write("    for (int i = 0; i < ret._1_size(); i++) {\n")
            if method["retType"]["key"]["IdlType"] == "struct":
                file.write("        serialize(ret._1(i), methodRet[i]);\n")
            else:
                if method["retType"]["key"]["IdlType"] == "enum":
                    file.write("        methodRet.push_back((" + self.file_name_split[0] + "_cpp::" + method["retType"]["key"]["type"] + ")ret._1(i));\n")
                else:
                    file.write("        methodRet.push_back(ret._1(i));\n")
            file.write("    }\n")
            file.write("    if (callback) { callback(methodRet, errorID); }\n") 
        elif method["retType"]["IdlType"] == "struct":
            file.write("    " + method["retType"]["type"] + " methodRet;\n")
            file.write("    serialize(ret._1(), methodRet);\n")
            file.write("    if (callback) { callback(methodRet, errorID); }\n") 
        elif method["retType"]["IdlType"] == "set":
            file.write("    " + method["retType"]["type"]  + "<" + method["retType"]["key"]["type"] + "> methodRet;\n")
            file.write("    for (int i = 0; i < ret._1_size(); i++) {\n")
            if method["retType"]["key"]["IdlType"] == "enum":
                file.write("        methodRet.insert((" + self.file_name_split[0] + "_cpp::" + method["retType"]["key"]["type"] + ")ret._1(i));\n")
            else:
                file.write("        methodRet.insert(ret._1(i));\n")
            file.write("    }\n")
            file.write("    if (callback) { callback(methodRet, errorID); }\n") 
        elif method["retType"]["IdlType"] == "dict":
            file.write("    " + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + "> methodRet;\n")
            file.write("    for (auto& [k, v] : ret._1()) {\n")
            if method["retType"]["value"]["IdlType"] == "struct":
                file.write("        serialize(v, methodRet[k]);\n")
            else:
                if method["retType"]["value"]["IdlType"] == "enum":
                    file.write("        methodRet[k] = (" + self.file_name_split[0] + "_cpp::" + method["retType"]["value"]["type"] + ")v;\n")
                else:
                    file.write("        methodRet[k] = v;\n")
            file.write("    }\n")
            file.write("    if (callback) { callback(methodRet, errorID); }\n") 
        else:
            if method["retType"]["IdlType"] == "void":
                file.write("    if (callback) { callback(errorID); }\n") 
            else:
                if method["retType"]["IdlType"] == "enum":
                    file.write("    if (callback) { callback((" + self.file_name_split[0] + "_cpp::" + method["retType"]["type"] + ")ret._1(), errorID); }\n") 
                else:
                    file.write("    if (callback) { callback(ret._1(), errorID); }\n") 
        file.write("}\n\n")
        
    def gen_method_ret_serialize(self, service, method, file):
        if service["type"] == "generic":
            return
        if "oneway" in method:
            return

        file.write("void serialize_stub_" + service["name"] + "_" + method["name"] + "_retval(rpc::StubCallPtr callPtr, const char* signature, rpc::ServiceID serviceID, rpc::ErrorID errorID")
        if method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
            fkey = method["retType"]["key"]["type"]
            if method["retType"]["key"]["IdlType"] == "enum":
                fkey = self.file_name_split[0] + "_cpp::" + fkey
            file.write(", std::shared_ptr<")
            file.write(method["retType"]["type"] + "<" + fkey + ">")
            file.write("> arg")
        elif method["retType"]["IdlType"] == "dict":
            fvalue = method["retType"]["value"]["type"]
            if method["retType"]["value"]["IdlType"] == "enum":
                fvalue = self.file_name_split[0] + "_cpp::" + fvalue

            file.write(", std::shared_ptr<")
            file.write(method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + fvalue + ">")
            file.write("> arg")
        elif method["retType"]["IdlType"] == "struct" or method["retType"]["IdlType"] == "string":
            file.write(", std::shared_ptr<")
            file.write(method["retType"]["type"])
            file.write("> arg")
        elif method["retType"]["IdlType"] == "void":
            pass
        elif method["retType"]["IdlType"] == "enum":
            file.write(", " + self.file_name_split[0] + "_cpp::" + method["retType"]["type"] + " arg")
        else:
            file.write(", " + method["retType"]["type"] + " arg")

        file.write(") {\n")
        file.write("    " + self.file_name_split[0] + "::" + service["name"] + "_" + method["name"] + "_ret ret;\n")
        if method["retType"]["IdlType"] != "void":
            if method["retType"]["IdlType"] == "enum":
                file.write("    if ((int)arg) {\n")
            else:
                file.write("    if (arg) {\n")
            if method["retType"]["IdlType"] == "seq":
                file.write("        for (auto& i : *arg) {\n")
                if method["retType"]["key"]["IdlType"] == "struct":
                    file.write("            serialize(i, *ret.add__1());\n")
                else:
                    if method["retType"]["key"]["IdlType"] == "enum":
                        file.write("            ret.add__1((" + self.file_name_split[0] + "::" + method["retType"]["key"]["type"] + ")i);\n")
                    else:
                        file.write("            ret.add__1(i);\n")
                file.write("        }\n")
            elif method["retType"]["IdlType"] == "set":
                file.write("        for (auto& i : *arg) {\n")
                if method["retType"]["key"]["IdlType"] == "enum":
                    file.write("            ret.add__1((" + self.file_name_split[0] + "::" + method["retType"]["key"]["type"] + ")i);\n")
                else:
                    file.write("            ret.add__1(i);\n")
                file.write("        }\n")
            elif method["retType"]["IdlType"] == "dict":
                file.write("        for (auto& [k, v] : *arg) {\n")
                if method["retType"]["value"]["IdlType"] == "struct":
                    file.write("            serialize(v, (*ret.mutable__1())[k]);\n")
                else:
                    if method["retType"]["value"]["IdlType"] == "enum":
                        file.write("            (*ret.mutable__1())[k] = (" + self.file_name_split[0] + "::" + method["retType"]["value"]["type"] + ")v;\n")
                    else:
                        file.write("            (*ret.mutable__1())[k] = v;\n")
                file.write("        }\n")
            elif method["retType"]["IdlType"] == "struct":
                file.write("        serialize(*arg, *ret.mutable__1());\n")
            elif method["retType"]["IdlType"] == "string":
                file.write("        ret.set__1(*arg);\n")
            else:
                if method["retType"]["IdlType"] == "enum":
                    file.write("        ret.set__1((" + self.file_name_split[0] + "::" + method["retType"]["type"] + ")arg);\n")
                else:
                    file.write("        ret.set__1(arg);\n")
            file.write("    }\n")
        file.write("    auto bodySize = ret.ByteSizeLong();\n")
        file.write("    if (callPtr->getGlobalIndex() == rpc::INVALID_GLOBAL_INDEX) {\n")
        file.write("        std::size_t length = sizeof(rpc::RpcRetHeader) + bodySize;\n")
        file.write("        auto* header = reinterpret_cast<rpc::RpcRetHeader*>(buffer);\n")
        file.write("        buildHeader(*header, callPtr, serviceID, errorID, bodySize);\n")
        file.write("        if (!ret.SerializeToArray(buffer + sizeof(rpc::RpcRetHeader), sizeof(buffer) - sizeof(rpc::RpcRetHeader))) {\n")
        file.write('            Logbegin << "Stub return value serializing failed, [" << signature << "]" << Logend;\n')
        file.write('            Logbegin << "Protobuf SerializeToArray failed, [" << ret.GetDescriptor()->name() << "]" << Logend;\n')
        file.write('            return;\n')
        file.write("        }\n")
        file.write("        callPtr->getTransport()->send(buffer, (int)length);\n")
        file.write("    } else {\n")
        file.write("        std::size_t length = sizeof(rpc::RpcProxyRetHeader) + bodySize;\n")
        file.write("        auto* header = reinterpret_cast<rpc::RpcProxyRetHeader*>(buffer);\n")
        file.write("        buildHeader(*header, callPtr, serviceID, errorID, bodySize);\n")
        file.write("        if (!ret.SerializeToArray(buffer + sizeof(rpc::RpcProxyRetHeader), sizeof(buffer) - sizeof(rpc::RpcProxyRetHeader))) {\n")
        file.write('            Logbegin << "Stub return value serializing failed, [" << signature << "]" << Logend;\n')
        file.write('            Logbegin << "Protobuf SerializeToArray failed, [" << ret.GetDescriptor()->name() << "]" << Logend;\n')
        file.write('            return;\n')
        file.write("        }\n")
        file.write("        callPtr->getTransport()->send(buffer, (int)length);\n")
        file.write("    }\n")
        file.write("}\n\n")
        
    def gen_method_stub_deserializer(self, service, method, file):
        if service["type"] == "generic":
            return
        file.write("void deserialize_stub_" + service["name"] + "_" + method["name"] + "(rpc::StubCallPtr callPtr")
        for arg in method["arguments"]:
            if arg["IdlType"] == "void":
                break
            if arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                fkey = arg["key"]["type"]
                if arg["key"]["IdlType"] == "enum":
                    fkey = self.file_name_split[0] + "_cpp::" + fkey
                file.write(", " + arg["type"] + "<" + fkey + ">& _" + str(arg["index"]))
            elif arg["IdlType"] == "dict":

                fvalue = arg["value"]["type"]
                if arg["value"]["IdlType"] == "enum":
                    fvalue = self.file_name_split[0] + "_cpp::" + fvalue

                file.write(", " + arg["type"] + "<" + arg["key"]["type"] + "," + fvalue + ">& _" + str(arg["index"]))
            elif arg["IdlType"] == "struct" or arg["IdlType"] == "string":
                file.write(", " + arg["type"] + " & _" + str(arg["index"]))
            elif arg["IdlType"] == "enum":
                file.write(", " + self.file_name_split[0] + "_cpp::" + arg["type"] + "& _" + str(arg["index"]))
            else:
                file.write(", " + arg["type"] + "& _" + str(arg["index"]))
        file.write(") {\n")
        file.write("    int size = 0;\n")
        file.write("    auto* data = callPtr->getData(size);\n")
        file.write("    " + self.file_name_split[0] + "::" + service["name"] + "_" + method["name"] + "_args args;\n")
        file.write("    if (!args.ParseFromArray(data, size)) {\n")
        file.write('        Logbegin << "Stub deserializing failed, [' + service["name"] + '::' + method["name"] +']" << Logend;\n')
        file.write('        Logbegin << "Protobuf ParseFromArray failed, [" << args.GetDescriptor()->name() << "]" << Logend;\n')
        file.write('        return;\n')
        file.write("    }\n")
        for arg in method["arguments"]:
            if arg["IdlType"] == "void":
                break
            if arg["IdlType"] == "seq":                
                if arg["key"]["IdlType"] == "struct":
                    file.write("    _" + str(arg["index"]) + ".resize(args._" +  str(arg["index"]) + "_size());\n")
                    file.write("    for (int i = 0; i < args._" + str(arg["index"]) + "_size(); i++) {\n")
                    file.write("        serialize(args._" +  str(arg["index"]) + "(i), _" + str(arg["index"]) + "[i]);\n")
                else:
                    file.write("    for (int i = 0; i < args._" + str(arg["index"]) + "_size(); i++) {\n")
                    if arg["key"]["IdlType"] == "enum":
                        file.write("        _" + str(arg["index"]) + ".push_back((" + self.file_name_split[0] + "_cpp::" + arg["key"]["type"] + ")args._" + str(arg["index"]) + "(i));\n")
                    else:
                        file.write("        _" + str(arg["index"]) + ".push_back(args._" + str(arg["index"]) + "(i));\n")
                file.write("    }\n")
            elif arg["IdlType"] == "set":
                file.write("    for (int i = 0; i < args._" + str(arg["index"]) + "_size(); i++) {\n")
                if arg["key"]["IdlType"] == "enum":
                    file.write("        _" + str(arg["index"]) + ".insert((" + self.file_name_split[0] + "_cpp::" + arg["key"]["type"] + ")args._" + str(arg["index"]) + "(i));\n")
                else:
                    file.write("        _" + str(arg["index"]) + ".insert(args._" + str(arg["index"]) + "(i));\n")
                file.write("    }\n")
            elif arg["IdlType"] == "dict":
                file.write("    for (auto& [k, v] : args._" + str(arg["index"]) + "()) {\n")
                if arg["value"]["IdlType"] == "struct":
                    file.write("        serialize(v, _" + str(arg["index"]) + "[k]);\n")
                else:
                    if arg["value"]["IdlType"] == "enum":
                        file.write("        _" + str(arg["index"]) + "[k] = (" + self.file_name_split[0] + "_cpp::" + arg["value"]["type"] + ")v;\n")
                    else:
                        file.write("        _" + str(arg["index"]) + "[k] = v;\n")
                file.write("    }\n")
            elif arg["IdlType"] == "struct":
                file.write("    serialize(args._" +  str(arg["index"]) + "(), _" + str(arg["index"]) + ");\n")
            else:
                if arg["IdlType"] == "enum":
                    file.write("    _" + str(arg["index"]) + " = (" + self.file_name_split[0] + "_cpp::" + arg["type"] + ")args._" + str(arg["index"]) + "();\n")
                else:
                    file.write("    _" + str(arg["index"]) + " = args._" + str(arg["index"]) + "();\n")
        file.write("}\n\n")
        
    def gen_method_ret_coro_deserialize(self, service, method, file):
        if "oneway" in method:
            return
        if method["retType"]["IdlType"]  == "seq":
            fkey = method["retType"]["key"]["type"] 
            if method["retType"]["key"]["IdlType"] == "enum":
                fkey = self.file_name_split[0] + "_cpp::" + fkey
            file.write("std::shared_ptr<" + method["retType"]["type"]  + "<" + fkey + ">>")
        elif method["retType"]["IdlType"] == "struct":
            file.write("std::shared_ptr<" + method["retType"]["type"] + ">")
        elif method["retType"]["IdlType"] == "set":
            fkey = method["retType"]["key"]["type"] 
            if method["retType"]["key"]["IdlType"] == "enum":
                fkey = self.file_name_split[0] + "_cpp::" + fkey
            file.write("std::shared_ptr<" + method["retType"]["type"]  + "<" + fkey + ">>")
        elif method["retType"]["IdlType"] == "dict":

            fvalue = method["retType"]["value"]["type"]
            if method["retType"]["value"]["IdlType"] == "enum":
                fvalue = self.file_name_split[0] + "_cpp::" + fvalue

            file.write("std::shared_ptr<" + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + fvalue + ">>")
        elif method["retType"]["IdlType"] == "enum":
            file.write( self.file_name_split[0] + "_cpp::"+method["retType"]["type"])
        else:
            file.write(method["retType"]["type"])
        file.write(" deserialize_" + service["name"] + "_" + method["name"] + "_coro(rpc::TransportPtr trans, int size) {\n")
        file.write("    " + self.file_name_split[0] + "::" + service["name"] + "_" + method["name"] + "_ret ret;\n")
        file.write("    trans->recv(buffer, size);\n")
        file.write("    if (!ret.ParseFromArray(buffer, size)) {\n")
        file.write('        Logbegin << "Proxy deserializing failed, [' + service["name"] + '::' + method["name"] +']" << Logend;\n')
        file.write('        Logbegin << "Protobuf ParseFromArray failed, [" << ret.GetDescriptor()->name() << "]" << Logend;\n')
        if method["retType"]["IdlType"] == "void":
            file.write("        return;\n")
        elif method["retType"]["IdlType"] == "string":
            file.write('        return "";\n')
        elif method["retType"]["IdlType"] == "bool":
            file.write('        return false;\n')
        elif method["retType"]["IdlType"] == "seq":
            file.write("        return std::shared_ptr<")
            file.write(method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ">")
            file.write(">();\n")
        elif method["retType"]["IdlType"] == "set":
            file.write("        return std::shared_ptr<")
            file.write(method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ">")
            file.write(">();\n")
        elif method["retType"]["IdlType"] == "seq":
            file.write("        return std::shared_ptr<")
            file.write(method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ">")
            file.write(">();\n")
        elif method["retType"]["IdlType"] == "dict":        
            file.write("        return std::shared_ptr<")
            file.write(method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + ">")
            file.write(">();\n")
        elif method["retType"]["IdlType"] == "struct":
            file.write("        return std::shared_ptr<")
            file.write(method["retType"]["type"])
            file.write(">();\n")
        elif method["retType"]["IdlType"] == "string":
            file.write("        return std::shared_ptr<")
            file.write(method["retType"]["type"])
            file.write(">();\n")
        else:
            if method["retType"]["IdlType"] == "enum":
                file.write("        return (" + self.file_name_split[0] + "_cpp::" + method["retType"]["type"] + ")0;\n")
            else:
                file.write("        return 0;\n")
        file.write("    }\n")
        if method["retType"]["IdlType"] == "void":
            file.write("    return;\n")
        else:
            if method["retType"]["IdlType"]  == "seq":
                file.write("    auto methodRet = rpc::make_shared_ptr<" + method["retType"]["type"]  + "<" + method["retType"]["key"]["type"] + ">>(ret._1_size());\n")
                file.write("    for (int i = 0; i < ret._1_size(); i++) {\n")
                if method["retType"]["key"]["IdlType"] == "struct":
                    file.write("        serialize(ret._1(i), (*methodRet)[i]);\n")
                else:
                    if method["retType"]["key"]["IdlType"] == "enum":
                        file.write("        methodRet->push_back((" + self.file_name_split[0] + "_cpp::" + method["retType"]["key"]["type"] + ")ret._1(i));\n")
                    else:
                        file.write("        methodRet->push_back(ret._1(i));\n")
                file.write("    }\n")
                file.write("    return methodRet;\n") 
            elif method["retType"]["IdlType"] == "struct":
                file.write("    auto methodRet = rpc::make_shared_ptr<" + method["retType"]["type"] + ">();\n")
                file.write("    serialize(ret._1(), *methodRet);\n")
                file.write("    return methodRet;\n")
            elif method["retType"]["IdlType"] == "set":
                file.write("    auto methodRet = rpc::make_shared_ptr<" + method["retType"]["type"]  + "<" + method["retType"]["key"]["type"] + ">>();\n")
                file.write("    for (int i = 0; i < ret._1_size(); i++) {\n")
                if method["retType"]["key"]["IdlType"] == "enum":
                    file.write("        methodRet->insert((" + self.file_name_split[0] + "_cpp::" + method["retType"]["key"]["type"] + ")ret._1(i));\n")
                else:
                    file.write("        methodRet->insert(ret._1(i));\n")
                file.write("    }\n")
                file.write("    return methodRet;\n") 
            elif method["retType"]["IdlType"] == "dict":
                file.write("    auto methodRet = rpc::make_shared_ptr<" + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + ">>();\n")
                file.write("    for (auto& [k, v] : ret._1()) {\n")
                if method["retType"]["value"]["IdlType"] == "struct":
                    file.write("        serialize(v, (*methodRet)[k]);\n")
                else:
                    if method["retType"]["value"]["IdlType"] == "enum":
                        file.write("        (*methodRet)[k] = (" + self.file_name_split[0] + "_cpp::" + method["retType"]["value"]["type"] + ")v;\n")
                    else:
                        file.write("        (*methodRet)[k] = v;\n")
                file.write("    }\n")
                file.write("    return methodRet;\n") 
            else:
                if method["retType"]["IdlType"] == "enum":
                    file.write("    return (" + self.file_name_split[0] + "_cpp::" + method["retType"]["type"] + ")ret._1();\n") 
                else:
                    file.write("    return ret._1();\n") 
        file.write("}\n\n")

if __name__ == "__main__":
    cppjson = json.load(open(sys.argv[1]))
    pbjson = json.load(open(sys.argv[2]))
    FileGenerator(sys.argv[1], cppjson, pbjson)
